import { EventEmitter2 } from "eventemitter2";

export type Hash = string;
export type Version = string;

export class Log {
    static info(...args: any[]) {
        console.log("[x.web]", ...args);
    }
}

export function hash(): Hash {
    return Math.random().toString(36).substring(2) + Math.random().toString(36).substring(2) + Math.random().toString(36).substring(2);
}

export class XPlugin extends EventEmitter2 {
    id = hash();
    plugins = new Array<XPlugin>();
    registry = new Map<String, Function>();
    host?: XPluginHost;

    constructor(public name: string, public version: Version, public firendlyName: string) {
        super();

        Log.info(`loading plugin ${name} on ${this.id}`, this);

        if (!(this instanceof XPluginRegistry)) {
            registry.pluginAdd(this);
        }

        Log.info(`loaded plugin ${this.id}`);
    }

    pluginAdd(plugin: XPlugin) {
        this.plugins.push(plugin);

        let host = this.host!;
        if (this instanceof XPluginHost) {
            host = this;
        }
        plugin.setHost(host);
        plugin.onInstall();
        this.emit("plugin-add", plugin);

        if (host.dom) {
            plugin.onHostInit();
        } else {
            host.on("host-init", plugin.onHostInit.bind(plugin));
        }
    }

    pluginRemove(id: Hash) {
        let plugin = this.plugins.find((p) => p.id == id);
        this.plugins = this.plugins.filter((p) => p != plugin);
        this.emit("plugin-remove", id);
        plugin?.onUninstall();
    }

    pluginInit(plugin: XPlugin) {
        this.emit("plugin-init", plugin);
    }

    setHost(host: XPluginHost) {
        this.host = host;
    }

    pluginCall(registryKey: string, ...args: any[]) {
        return this.registry.get(registryKey)?.apply(this, args);
    }

    onHostInit() {}
    onInstall() {}
    onUninstall() {}
}

export class XPluginHost extends XPlugin {
    dom?: HTMLElement;
    hostInit(dom: HTMLElement) {
        this.dom = dom;
        this.emit("host-init", dom);
    }
}

export class XPluginRegistry extends EventEmitter2 {
    plugins = new Array<XPlugin>();

    constructor() {
        super();
        Log.info("create plugin registry");
    }

    pluginAdd(plugin: XPlugin) {
        this.plugins.push(plugin);
        this.emit("plugin-add", plugin);
    }

    pluginRemove(id: Hash) {
        this.plugins = this.plugins.filter((p) => p.id != id);
        this.emit("plugin-remove", id);
    }
}

const registry = new XPluginRegistry();

export { registry };
